//! PWM controller platform wrapper for RK3588.

use kspin::SpinNoIrq;
use pwm_rk3588::{Alignment, Config as PwmConfig, Polarity, RockchipPwm, RunMode, TickConfig};

use crate::mem::phys_to_virt;

/// Physical base addresses for the PMU PWM channels.
pub const PWM0_BASE_PADDR: usize = 0xFD8B0000;
pub const PWM1_BASE_PADDR: usize = 0xFD8B0010;
pub const PWM2_BASE_PADDR: usize = 0xFD8B0020;
pub const PWM3_BASE_PADDR: usize = 0xFD8B0030;

/// The PMU PWM block on RK3588 uses a 24MHz reference clock.
pub const PWM_PMU_CLOCK_HZ: u64 = 24_000_000;

/// Number of PWM channels exposed by this controller.
pub const PWM_CHANNEL_COUNT: usize = 4;

/// Wrapper around the PWM instances present on the LubanCat 5 BTB.
pub struct PwmController {
    channels: [SpinNoIrq<RockchipPwm>; PWM_CHANNEL_COUNT],
}

impl PwmController {
    /// Creates a controller with PWM0..PWM3 mapped into the kernel virtual
    /// address space.
    pub const fn new() -> Self {
        let pwm0_vaddr = phys_to_virt(axplat::mem::pa!(PWM0_BASE_PADDR)).as_usize();
        let pwm1_vaddr = phys_to_virt(axplat::mem::pa!(PWM1_BASE_PADDR)).as_usize();
        let pwm2_vaddr = phys_to_virt(axplat::mem::pa!(PWM2_BASE_PADDR)).as_usize();
        let pwm3_vaddr = phys_to_virt(axplat::mem::pa!(PWM3_BASE_PADDR)).as_usize();

        Self {
            channels: [
                SpinNoIrq::new(RockchipPwm::new(pwm0_vaddr, PWM_PMU_CLOCK_HZ)),
                SpinNoIrq::new(RockchipPwm::new(pwm1_vaddr, PWM_PMU_CLOCK_HZ)),
                SpinNoIrq::new(RockchipPwm::new(pwm2_vaddr, PWM_PMU_CLOCK_HZ)),
                SpinNoIrq::new(RockchipPwm::new(pwm3_vaddr, PWM_PMU_CLOCK_HZ)),
            ],
        }
    }

    /// Returns the number of exposed channels.
    #[inline]
    pub const fn len(&self) -> usize {
        PWM_CHANNEL_COUNT
    }

    /// Retrieves a spinlock-wrapped PWM instance by index.
    ///
    /// # Parameters
    /// - `index`: PWM channel number (0-3).
    pub fn channel(&self, index: usize) -> Option<&SpinNoIrq<RockchipPwm>> {
        self.channels.get(index)
    }
}

/// Global PWM controller instance.
static PWM_CONTROLLER: PwmController = PwmController::new();

/// Returns the global PWM controller.
#[inline]
pub fn pwm_controller() -> &'static PwmController {
    &PWM_CONTROLLER
}

/// Initializes the PWM controller.
///
/// Currently this function only logs the detected features, mirroring the Linux
/// driver behaviour that validates register accessibility during probe.
pub fn init() {
    for (index, channel) in PWM_CONTROLLER.channels.iter().enumerate() {
        let pwm = channel.lock();
        let state = pwm.state();
        log::info!(
            "PWM{} initialized: period={} duty={} enabled={} alignment={:?} mode={:?}",
            index,
            state.period,
            state.duty,
            state.enabled,
            state.alignment,
            state.mode
        );
    }
}

/// Convenience helper for building PWM configurations.
pub fn make_config(
    period_ns: u64,
    duty_ns: u64,
    polarity: Polarity,
    alignment: Alignment,
    mode: RunMode,
    enable: bool,
) -> PwmConfig {
    PwmConfig {
        period_ns,
        duty_ns,
        polarity,
        alignment,
        mode,
        enable,
    }
}

/// Convenience helper for tick-based configurations.
pub fn make_tick_config(
    period: u32,
    duty: u32,
    polarity: Polarity,
    alignment: Alignment,
    mode: RunMode,
    enable: bool,
) -> TickConfig {
    TickConfig {
        period,
        duty,
        polarity,
        alignment,
        mode,
        enable,
    }
}
